﻿/*
 * MapLegend
 * 
 * initial implementation : 20.10.2008
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see Petra.Gis.License.txt
 */
using System;
using System.Collections;
using System.Collections.Generic;
using Petra.Gis.Geometry;
using Petra.Gis.DataServices;
using Petra.Gis.DataServices.Metadata;
using Petra.Gis.ReferenceSystemServices;
using Petra.Gis.ReferenceSystemServices.Transformations;
using Petra.Gis.RenderingServices.PlanarBase.Support;


namespace Petra.Gis.RenderingServices.PlanarBase 
{


  /// <summary>
  /// MapLegend is root node of all entries in legend that should be rendered in map.
  /// <para>
  /// this is abstract class despite there is not so much to change or re-implement for
  /// specific rendering engine or technology. Legend simply remains always same.
  /// </para>
  /// </summary>
  public abstract class MapLegend : LeafLegendNode
  {

    //protected 


    #region ctor


    /// <summary>
    /// xaml
    /// </summary>
    public MapLegend()
      : base()
    {
      Title = "Legend";
      List<RenderableLegendNodeBase> tmp = new List<RenderableLegendNodeBase>();
      //TreeItem.setInternalDisplaOrder(ChildNodes, tmp);
      DisplayOrder = new DisplayOrder(this, tmp);
      __registerProperty(this, "internalDisplayOrder");
      __setPropetyValue(this, "writableDisplayOrder", tmp);
      
    }




    public MapLegend(MapBase map)
      : base(null, "Legend")
    {
      Map = map;

      List<RenderableLegendNodeBase> tmp = new List<RenderableLegendNodeBase>();
      //TreeItem.setInternalDisplaOrder(ChildNodes, tmp);
      DisplayOrder = new DisplayOrder(this, tmp);
      __registerProperty(this, "internalDisplayOrder");
      __setPropetyValue(this, "writableDisplayOrder", tmp);
    }


    #endregion ctor



    /// <summary>
    /// handles extra functions when 
    /// this legend is assigned to new/another map.
    /// this method resolves MSFT shits about xaml,
    /// as xaml is not avail on SL
    /// </summary>
    /// <param name="newMap"></param>
    public virtual void AttachToMap(MapBase newMap)
    {
      //at this moment just assign local variables...
      this.Map = newMap;
    }




    //private MapBase _map;

    /// <summary>
    /// returns map driven by this legend
    /// </summary>
    //[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public MapBase Map
    { get; protected set; }




    //private DisplayOrder _displayOrder;

    /// <summary>
    /// returns display order List.
    /// This is List of all items in legend ordered top-down,
    /// where first item on DisplayOrder List is rendered in map topmost.
    /// </summary>
    public DisplayOrder DisplayOrder
    { get; protected set; }





    /// <summary>
    /// creates leaf node that contains no data but hold place for subNodes. This node is not legend visible
    /// and must be appended into legendChildNodes.
    /// </summary>
    /// <param name="title"></param>
    /// <returns></returns>
    public LegendNodeBase CreateLegendNode(string title)
    {
      return new LeafLegendNode(this, title);
    }




    //public DataLegendNodeBase CreateLegendNode(SpatialDbCommand dsc)
    //{
    //  //create unique default name

    //  if (!dsc.Extensions.ContainsKey("FeatureMetadata"))
    //    throw new ArgumentException("no spatial metadata in SpatialDbCommand, cand append to legend");

    //  FeatureMetadata m = (FeatureMetadata)dsc.Extensions["FeatureMetadata"];
    //  if(m == null)
    //    throw new ArgumentException("null spatial metadata in SpatialDbCommand, cand append to legend");

    //  // prepare transform for this data..
    //  prepareTransforms(m);


    //  DataLegendNodeBase tmpNode = new CommandLegendNode(null, dsc, m.Name);

    //  //_displayOrder.Insert(0, tmpNode);

    //  return tmpNode;
    //}




#if !SILVERLIGHT

    /// <summary>
    /// creates new <c>CommandLegendNode</c> for given command and renderer.  This node is not legend visible
    /// and must be appended into legendChildNodes.
    /// </summary>
    /// <param name="dsc">SpatialDbCommand</param>
    /// <param name="geomRenderers">renderer</param>
    /// <returns>new <c>CommandLegendNode</c></returns>
    public CommandLegendNode CreateLegendNode(SpatialDbCommand featureSet, params RendererBase[] renderers)
    {

      if (featureSet.Metadata == null)
        throw new ArgumentException("null spatial metadata in SpatialDbCommand, can't append to legend");

      // prepare transform for this data..
      prepareTransforms(featureSet.Metadata);

      return new CommandLegendNode(this, featureSet, featureSet.Metadata.Name, renderers);

    }

#endif





    /// <summary>
    /// creates new <c>ObjectLegendNode</c> from FeatureSetBase. This node is not legend visible
    /// and must be appended into legendChildNodes.
    /// </summary>
    /// <param name="featureSet">memory feature set with IEnumerable collection of geometries</param>
    /// <param name="geomRenderers">set of renderers to use in map for geometry rendering</param>
    /// <returns>new, initalized instance of ObjectLegendNode</returns>
    public ObjectLegendNode CreateLegendNode(FeatureSetBase featureSet, params RendererBase[] renderers)
    {

      if (featureSet.Metadata == null)
        throw new ArgumentException("null spatial metadata supplied, can't append to legend");

      foreach (object o in featureSet.Data)
      {
        if (o.GetType() != typeof(GeometryBase) && featureSet.GeometryConverter == null)
          throw new ArgumentException(@"IEnumerable must contain collection of GeometryBase in this case
                                       otherwise use CreateLegendNode with IGeometryConverter parameter");
        break;
      }

      //geometry converter probe...
      if(featureSet.GeometryConverter != null)
        foreach (object o in featureSet.Data)
        {
          //this throws error if converter or featureSet.Data is wrong...
          featureSet.GeometryConverter.CreateGeometry(o,featureSet.Metadata.GeometryFieldsMetadata[0].Name);
          break;
        }

      ObjectLegendNode tmpNode = new ObjectLegendNode(this, featureSet, featureSet.Metadata.Name, renderers);

      //_displayOrder.Insert(0, tmpNode);

      return tmpNode;
    
    }




    //public abstract object DrawLegend();



    /// <summary>
    /// prepares transform for given feature metadata
    /// tries to find transform in all available.
    /// Throws error if transform not found.
    /// </summary>
    /// <param name="m">new feature metadata</param>
    protected void prepareTransforms(FeatureMetadata m)
    {
      foreach (SpatialFieldMetadata gfm in m.GeometryFieldsMetadata)
      {
        // prepare reverse transform for this geometry field coordinate system...
        if (this.Legend.Map.SRSTransformCache.GetTransform(gfm.SRS, true) == null)
          throw new ApplicationException("unsupported coordinate system in " +
          m.Name + ", system cant find transformation for " + gfm.SRS.Metadata.Name);
      }
    }



  }

}
